<?php
// PHP Weathermap 0.97
// Copyright Howard Jones, 2005-2010 howie@thingy.com
// http://www.network-weathermap.com/
// Released under the GNU Public License


class WeatherMapNode extends WeatherMapItem
{
	var $owner;
	var $id;
	var $x,	$y;
	var $original_x, $original_y,$relative_resolved;
	var $width, $height;
	var $label, $proclabel, $labelfont;
	var $labelangle;
	var $name;
	var $infourl = array();
	var $notes;
	var $colours = array();
	var $overliburl;
	var $overlibwidth, $overlibheight;
	var $overlibcaption = array();
	var $maphtml;
	var $selected = 0;
	var $iconfile, $iconscalew, $iconscaleh;
	var $targets = array();
	var $bandwidth_in, $bandwidth_out;
	var $inpercent, $outpercent;
	var $max_bandwidth_in, $max_bandwidth_out;
	var $max_bandwidth_in_cfg, $max_bandwidth_out_cfg;
	var $labeloffset, $labeloffsetx, $labeloffsety;

	var $inherit_fieldlist;

	var $labelbgcolour;
	var $labeloutlinecolour;
	var $labelfontcolour;
	var $labelfontshadowcolour;
	var $cachefile;
	var $usescale;
	var $useiconscale;
	var $scaletype, $iconscaletype;
	var $inscalekey,$outscalekey;
	var $inscaletag, $outscaletag;
	# var $incolour,$outcolour;
	var $scalevar, $iconscalevar;
	var $notestext = array();
	var $image;
	var $centre_x, $centre_y;
	var $relative_to;
	var $zorder;
	var $template;
	var $polar;
	var $boundingboxes=array();

	function WeatherMapNode()
	{
		$this->inherit_fieldlist=array
			(
				'boundingboxes'=>array(),
				'my_default' => NULL,
				'label' => '',
				'proclabel' => '',
				'usescale' => 'DEFAULT',
				'scaletype' => 'percent',
				'iconscaletype' => 'percent',
				'useiconscale' => 'none',
				'scalevar' => 'in',
				'template' => ':: DEFAULT ::',
				'iconscalevar' => 'in',
				'labelfont' => 3,
				'relative_to' => '',
				'relative_resolved' => FALSE,
				'x' => NULL,
				'y' => NULL,
				'inscalekey'=>'', 'outscalekey'=>'',
				#'incolour'=>-1,'outcolour'=>-1,
				'original_x' => 0,
				'original_y' => 0,
				'inpercent'=>0,
				'outpercent'=>0,
				'labelangle'=>0,
				'iconfile' => '',
				'iconscalew' => 0,
				'iconscaleh' => 0,
				'targets' => array(),
				'infourl' => array(IN=>'',OUT=>''),
				'notestext' => array(IN=>'',OUT=>''),
				'notes' => array(),
				'hints' => array(),
				'overliburl' => array(IN=>array(),OUT=>array()),
				'overlibwidth' => 0,
				'overlibheight' => 0,
				'overlibcaption' => array(IN=>'',OUT=>''),
				'labeloutlinecolour' => array(0, 0, 0),
				'labelbgcolour' => array(255, 255, 255),
				'labelfontcolour' => array(0, 0, 0),
				'labelfontshadowcolour' => array(-1, -1, -1),
				'aiconoutlinecolour' => array(0,0,0),
				'aiconfillcolour' => array(-2,-2,-2), // copy from the node label
				'labeloffset' => '',
				'labeloffsetx' => 0,
				'labeloffsety' => 0,
				'zorder' => 600,
				'max_bandwidth_in' => 100,
				'max_bandwidth_out' => 100,
				'max_bandwidth_in_cfg' => '100',
				'max_bandwidth_out_cfg' => '100'
			);

		$this->width = 0;
		$this->height = 0;
		$this->centre_x = 0;
		$this->centre_y = 0;
		$this->polar = FALSE;
		$this->image = NULL;
	}

	function my_type() {  return "NODE"; }

	// make a mini-image, containing this node and nothing else
	// figure out where the real NODE centre is, relative to the top-left corner.
	function pre_render($im, &$map)
	{
		// don't bother drawing if there's no position - it's a template
		if( is_null($this->x) ) return;
		if( is_null($this->y) ) return;
		
		// apparently, some versions of the gd extension will crash
		// if we continue...
		if($this->label == '' && $this->iconfile=='') return;

		// start these off with sensible values, so that bbox
		// calculations are easier.
		$icon_x1 = $this->x; $icon_x2 = $this->x;
		$icon_y1 = $this->y; $icon_y2 = $this->y;
		$label_x1 = $this->x; $label_x2 = $this->x;
		$label_y1 = $this->y; $label_y2 = $this->y;
		$boxwidth = 0; $boxheight = 0;
		$icon_w = 0;
		$icon_h = 0;

		$col = new Colour(-1,-1,-1);
		# print $col->as_string();

		// if a target is specified, and you haven't forced no background, then the background will
		// come from the SCALE in USESCALE
		if( !empty($this->targets) && $this->usescale != 'none' )
		{
			$pc = 0;

			if($this->scalevar == 'in')
			{
				$pc = $this->inpercent;
				$col = $this->colours[IN];

			}
			
			if($this->scalevar == 'out')
			{
				$pc = $this->outpercent;
				$col = $this->colours[OUT];

			}
		}
		elseif($this->labelbgcolour != array(-1,-1,-1))
		{
			// $col=myimagecolorallocate($node_im, $this->labelbgcolour[0], $this->labelbgcolour[1], $this->labelbgcolour[2]);
			$col = new Colour($this->labelbgcolour);
		}

		$colicon = null;
		if ( !empty($this->targets) && $this->useiconscale != 'none' )
		{
			debug("Colorising the icon\n");
			$pc = 0;
			$val = 0;

			if($this->iconscalevar == 'in')
			{
				$pc = $this->inpercent;
				$col = $this->colours[IN];
				$val = $this->bandwidth_in;
			}
			if($this->iconscalevar == 'out')
			{
				$pc = $this->outpercent;
				$col = $this->colours[OUT];
				$val = $this->bandwidth_out;
			}

			if($this->iconscaletype=='percent')
			{
				list($colicon,$node_iconscalekey,$icontag) = 
					$map->NewColourFromPercent($pc, $this->useiconscale,$this->name );	
			}
			else
			{
				// use the absolute value if we aren't doing percentage scales.
				list($colicon,$node_iconscalekey,$icontag) = 
					$map->NewColourFromPercent($val, $this->useiconscale,$this->name, FALSE );
			}
		}

		// figure out a bounding rectangle for the label
		if ($this->label != '')
		{
			$padding = 4.0;
			$padfactor = 1.0;

			$this->proclabel = $map->ProcessString($this->label,$this,TRUE,TRUE);
			
			// if screenshot_mode is enabled, wipe any letters to X and wipe any IP address to 127.0.0.1
			// hopefully that will preserve enough information to show cool stuff without leaking info
			if($map->get_hint('screenshot_mode')==1)  $this->proclabel = screenshotify($this->proclabel);
			
			list($strwidth, $strheight) = $map->myimagestringsize($this->labelfont, $this->proclabel);

			if($this->labelangle==90 || $this->labelangle==270)
			{
				$boxwidth = ($strheight * $padfactor) + $padding;
				$boxheight = ($strwidth * $padfactor) + $padding;
				debug ("Node->pre_render: ".$this->name." Label Metrics are: $strwidth x $strheight -> $boxwidth x $boxheight\n");
				
				$label_x1 = $this->x - ($boxwidth / 2);
				$label_y1 = $this->y - ($boxheight / 2);

				$label_x2 = $this->x + ($boxwidth / 2);
				$label_y2 = $this->y + ($boxheight / 2);

				if($this->labelangle==90)
				{
					$txt_x = $this->x + ($strheight / 2);
					$txt_y = $this->y + ($strwidth / 2);
				}
				if($this->labelangle==270)
				{
					$txt_x = $this->x - ($strheight / 2);
					$txt_y = $this->y - ($strwidth / 2);
				}
			}
			
			if($this->labelangle==0 || $this->labelangle==180)
			{
				$boxwidth = ($strwidth * $padfactor) + $padding;
				$boxheight = ($strheight * $padfactor) + $padding;
				debug ("Node->pre_render: ".$this->name." Label Metrics are: $strwidth x $strheight -> $boxwidth x $boxheight\n");
							
				$label_x1 = $this->x - ($boxwidth / 2);
				$label_y1 = $this->y - ($boxheight / 2);

				$label_x2 = $this->x + ($boxwidth / 2);
				$label_y2 = $this->y + ($boxheight / 2);

				$txt_x = $this->x - ($strwidth / 2);
				$txt_y = $this->y + ($strheight / 2);
				
				if($this->labelangle==180)
				{
					$txt_x = $this->x + ($strwidth / 2);
					$txt_y = $this->y - ($strheight / 2);
				}

				# $this->width = $boxwidth;
				# $this->height = $boxheight;
			}
			$map->nodes[$this->name]->width = $boxwidth;
			$map->nodes[$this->name]->height = $boxheight;

			# print "TEXT at $txt_x , $txt_y\n";
		}

		// figure out a bounding rectangle for the icon
		if ($this->iconfile != '')
		{
			$icon_im = NULL;
			$icon_w = 0;
			$icon_h = 0;

			if($this->iconfile == 'rbox' || $this->iconfile == 'box' || $this->iconfile == 'round' || $this->iconfile == 'inpie' || $this->iconfile == 'outpie' || $this->iconfile == 'gauge' || $this->iconfile == 'nink')
			{
				debug("Artificial Icon type " .$this->iconfile. " for $this->name\n");
				// this is an artificial icon - we don't load a file for it

				$icon_im = imagecreatetruecolor($this->iconscalew,$this->iconscaleh);
				imageSaveAlpha($icon_im, TRUE);

				$nothing=imagecolorallocatealpha($icon_im,128,0,0,127);
				imagefill($icon_im, 0, 0, $nothing);

				$fill = NULL;
				$ink = NULL;

				$aifill = new Colour($this->aiconfillcolour);
				$aiink = new Colour($this->aiconoutlinecolour);

				if ( $aifill->is_copy() && !$col->is_none() )
				{
					$fill = $col;
				}
				else
				{
					if($aifill->is_real())
					{
						$fill = $aifill;
					}
				}

				if ($this->aiconoutlinecolour != array(-1,-1,-1))
				{
					$ink=$aiink;
				}

				if($this->iconfile=='box')
				{
					if($fill !== NULL && !$fill->is_none())
					{
						imagefilledrectangle($icon_im, 0, 0, $this->iconscalew-1, $this->iconscaleh-1, $fill->gdallocate($icon_im) );
					}

					if($ink !== NULL && !$ink->is_none())
					{
						imagerectangle($icon_im, 0, 0, $this->iconscalew-1, $this->iconscaleh-1, $ink->gdallocate($icon_im) );
					}
				}

				if($this->iconfile=='rbox')
				{
					if($fill !== NULL && !$fill->is_none())
					{
						imagefilledroundedrectangle($icon_im, 0, 0, $this->iconscalew-1, $this->iconscaleh-1, 4, $fill->gdallocate($icon_im) );
					}

					if($ink !== NULL && !$ink->is_none())
					{
						imageroundedrectangle($icon_im, 0, 0, $this->iconscalew-1, $this->iconscaleh-1, 4, $ink->gdallocate($icon_im) );
					}
				}

				if($this->iconfile=='round')
				{
					$rx = $this->iconscalew/2-1;
					$ry = $this->iconscaleh/2-1;

					if($fill !== NULL && !$fill->is_none() )
					{
						imagefilledellipse($icon_im,$rx,$ry,$rx*2,$ry*2,$fill->gdallocate($icon_im) );
					}

					if($ink !== NULL && !$ink->is_none())
					{
						imageellipse($icon_im,$rx,$ry,$rx*2,$ry*2,$ink->gdallocate($icon_im) );
					}
				}

				if($this->iconfile=='nink')
				{ 
					// print "NINK **************************************************************\n";
					$rx = $this->iconscalew/2-1;
					$ry = $this->iconscaleh/2-1;
					$size = $this->iconscalew;
					$quarter = $size/4;				
					
					$col1 = $this->colours[IN];
					$col2 = $this->colours[OUT];
					
					assert('!is_null($col1)');
					assert('!is_null($col2)');
					
					imagefilledarc($icon_im, $rx-1, $ry, $size, $size, 270,90, $col1->gdallocate($icon_im), IMG_ARC_PIE);
					imagefilledarc($icon_im, $rx+1, $ry, $size, $size, 90,270, $col2->gdallocate($icon_im), IMG_ARC_PIE);

					imagefilledarc($icon_im, $rx-1, $ry+$quarter, $quarter*2, $quarter*2, 0,360, $col1->gdallocate($icon_im), IMG_ARC_PIE);
					imagefilledarc($icon_im, $rx+1, $ry-$quarter, $quarter*2, $quarter*2, 0,360, $col2->gdallocate($icon_im), IMG_ARC_PIE);
					
					if($ink !== NULL && !$ink->is_none())
					{
						// XXX - need a font definition from somewhere for NINK text
						$font = 1;
						
						$instr = $map->ProcessString("{node:this:bandwidth_in:%.1k}",$this);
						$outstr = $map->ProcessString("{node:this:bandwidth_out:%.1k}",$this);
																		
						list($twid,$thgt) = $map->myimagestringsize($font,$instr);
						$map->myimagestring($icon_im, $font, $rx - $twid/2, $ry- $quarter + ($thgt/2),$instr,$ink->gdallocate($icon_im));
						
						list($twid,$thgt) = $map->myimagestringsize($font,$outstr);
						$map->myimagestring($icon_im, $font, $rx - $twid/2,  $ry + $quarter + ($thgt/2),$outstr,$ink->gdallocate($icon_im));
						
						imageellipse($icon_im,$rx,$ry,$rx*2,$ry*2,$ink->gdallocate($icon_im) );	
						// imagearc($icon_im, $rx,$ry,$quarter*4,$quarter*4, 0,360, $ink->gdallocate($icon_im));
					}
					// print "NINK ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n";
				}
				
				// XXX - needs proper colours
				if($this->iconfile=='inpie' || $this->iconfile=='outpie')
				{ 
					# list($colpie,$node_iconscalekey,$icontag) = $map->NewColourFromPercent($pc, $this->useiconscale,$this->name);
					
					if($this->iconfile=='inpie') $segment_angle = (($this->inpercent)/100) * 360;
					if($this->iconfile=='outpie') $segment_angle = (($this->outpercent)/100) * 360;
					
					$rx = $this->iconscalew/2-1;
					$ry = $this->iconscaleh/2-1;
										
					if($fill !== NULL && !$fill->is_none() )
					{
						imagefilledellipse($icon_im, $rx, $ry, $rx*2, $ry*2, $fill->gdallocate($icon_im) );
					}
					
					if($ink !== NULL && !$ink->is_none())
					{
						// imagefilledarc  ( resource $image  , int $cx  , int $cy  , int $width  , int $height  , int $start  , int $end  , int $color  , int $style  )
						imagefilledarc($icon_im, $rx, $ry, $rx*2,$ry*2, 0, $segment_angle, $ink->gdallocate($icon_im) , IMG_ARC_PIE);
					}
					
					if($fill !== NULL && !$fill->is_none() )
					{
						imageellipse($icon_im, $rx, $ry, $rx*2, $ry*2, $fill->gdallocate($icon_im) );
					}
					
					// warn('inpie AICON not implemented yet [WMWARN99]'); 
				}
				
				// if($this->iconfile=='outpie') { warn('outpie AICON not implemented yet [WMWARN99]'); }
				if($this->iconfile=='gauge') { warn('gauge AICON not implemented yet [WMWARN99]'); }

			}
			else
			{
				$this->iconfile = $map->ProcessString($this->iconfile ,$this);
				if (is_readable($this->iconfile))
				{
					imagealphablending($im, true);
					// draw the supplied icon, instead of the labelled box

					$icon_im = imagecreatefromfile($this->iconfile);
					# $icon_im = imagecreatefrompng($this->iconfile);
					if(function_exists("imagefilter") && isset($colicon) && $this->get_hint("use_imagefilter")==1)
					{						
						imagefilter($icon_im, IMG_FILTER_COLORIZE, $colicon->r, $colicon->g, $colicon->b);						
					}
					else
					{
                                            if(isset($colicon))
                                            {
                                                // debug("Skipping unavailable imagefilter() call.\n");
                                                imagecolorize($icon_im, $colicon->r, $colicon->g, $colicon->b);
                                            }
					}

					debug("If this is the last thing in your logs, you probably have a buggy GD library. Get > 2.0.33 or use PHP builtin.\n");
					if ($icon_im)
					{
						$icon_w = imagesx($icon_im);
						$icon_h = imagesy($icon_im);

						if(($this->iconscalew * $this->iconscaleh) > 0)
						{
							imagealphablending($icon_im, true);

							debug("SCALING ICON here\n");
							if($icon_w > $icon_h)
							{
								$scalefactor = $icon_w/$this->iconscalew;
							}
							else
							{
								$scalefactor = $icon_h/$this->iconscaleh;
							}
							$new_width = $icon_w / $scalefactor;
							$new_height = $icon_h / $scalefactor;
							$scaled = imagecreatetruecolor($new_width, $new_height);
							imagealphablending($scaled,false);
							imagecopyresampled($scaled, $icon_im, 0, 0, 0, 0, $new_width, $new_height, $icon_w, $icon_h);
							imagedestroy($icon_im);
							$icon_im = $scaled;

						}
					}
					else { warn ("Couldn't open PNG ICON: '" . $this->iconfile . "' - is it a PNG, JPEG or GIF?\n"); }
				}
				else
				{
					if($this->iconfile != 'none')
					{
						warn ("ICON '" . $this->iconfile . "' does not exist, or is not readable. Check path and permissions.\n");
					}
				}
			}

			if($icon_im)
			{
				$icon_w = imagesx($icon_im);
				$icon_h = imagesy($icon_im);

				$icon_x1 = $this->x - $icon_w / 2;
				$icon_y1 = $this->y - $icon_h / 2;
				$icon_x2 = $this->x + $icon_w / 2;
				$icon_y2 = $this->y + $icon_h / 2;

				$map->nodes[$this->name]->width = imagesx($icon_im);
				$map->nodes[$this->name]->height = imagesy($icon_im);

				// $map->imap->addArea("Rectangle", "NODE:" . $this->name . ':0', '', array($icon_x1, $icon_y1, $icon_x2, $icon_y2));
				$map->nodes[$this->name]->boundingboxes[] = array($icon_x1, $icon_y1, $icon_x2, $icon_y2);
			}

		}



		// do any offset calculations
		$dx=0;
		$dy=0;
		if ( ($this->labeloffset != '') && (($this->iconfile != '')) )
		{
			$this->labeloffsetx = 0;
			$this->labeloffsety = 0;

			list($dx, $dy) = calc_offset($this->labeloffset,
				($icon_w + $boxwidth -1),
				($icon_h + $boxheight)
			);

			#$this->labeloffsetx = $dx;
			#$this->labeloffsety = $dy;

		}

		$label_x1 += ($this->labeloffsetx + $dx);
		$label_x2 += ($this->labeloffsetx + $dx);
		$label_y1 += ($this->labeloffsety + $dy);
		$label_y2 += ($this->labeloffsety + $dy);

		if($this->label != '')
		{
			// $map->imap->addArea("Rectangle", "NODE:" . $this->name .':1', '', array($label_x1, $label_y1, $label_x2, $label_y2));
			$map->nodes[$this->name]->boundingboxes[] = array($label_x1, $label_y1, $label_x2, $label_y2);
		}

		// work out the bounding box of the whole thing

		$bbox_x1 = min($label_x1,$icon_x1);
		$bbox_x2 = max($label_x2,$icon_x2)+1;
		$bbox_y1 = min($label_y1,$icon_y1);
		$bbox_y2 = max($label_y2,$icon_y2)+1;

		#           imagerectangle($im,$bbox_x1,$bbox_y1,$bbox_x2,$bbox_y2,$map->selected);
		#         imagerectangle($im,$label_x1,$label_y1,$label_x2,$label_y2,$map->black);
		#       imagerectangle($im,$icon_x1,$icon_y1,$icon_x2,$icon_y2,$map->black);

		// create TWO imagemap entries - one for the label and one for the icon
		// (so we can have close-spaced icons better)


		$temp_width = $bbox_x2-$bbox_x1;
		$temp_height = $bbox_y2-$bbox_y1;
		// create an image of that size and draw into it
		$node_im=imagecreatetruecolor($temp_width,$temp_height );
		// ImageAlphaBlending($node_im, FALSE);
		imageSaveAlpha($node_im, TRUE);

		$nothing=imagecolorallocatealpha($node_im,128,0,0,127);
		imagefill($node_im, 0, 0, $nothing);

		#$col = $col->gdallocate($node_im);

		// imagefilledrectangle($node_im,0,0,$temp_width,$temp_height,  $nothing);

		$label_x1 -= $bbox_x1;
		$label_x2 -= $bbox_x1;
		$label_y1 -= $bbox_y1;
		$label_y2 -= $bbox_y1;

		$icon_x1 -= $bbox_x1;
		$icon_x2 -= $bbox_x1;
		$icon_y1 -= $bbox_y1;
		$icon_y2 -= $bbox_y1;


		// Draw the icon, if any
		if(isset($icon_im))
		{
			imagecopy($node_im, $icon_im, $icon_x1, $icon_y1, 0, 0, imagesx($icon_im), imagesy($icon_im));
			imagedestroy($icon_im);
		}

		// Draw the label, if any
		if ($this->label != '')
		{
			$txt_x -= $bbox_x1;
			$txt_x += ($this->labeloffsetx + $dx);
			$txt_y -= $bbox_y1;
			$txt_y += ($this->labeloffsety + $dy);

			#       print "FINAL TEXT at $txt_x , $txt_y\n";

			// if there's an icon, then you can choose to have no background

			if(! $col->is_none() )
			{
			    //imagefilledrectangle($node_im, $label_x1, $label_y1, $label_x2, $label_y2, $col->gdallocate($node_im));
			}

			if ($this->selected)
			{
				imagerectangle($node_im, $label_x1, $label_y1, $label_x2, $label_y2, $map->selected);
				// would be nice if it was thicker, too...
				wimagerectangle($node_im, $label_x1 + 1, $label_y1 + 1, $label_x2 - 1, $label_y2 - 1, $map->selected);
			}
			else
			{
				$olcol = new Colour($this->labeloutlinecolour);
				if ($olcol->is_real())
				{
					//imagerectangle($node_im, $label_x1, $label_y1, $label_x2, $label_y2, $olcol->gdallocate($node_im));
				}
			}
			#}

			$shcol = new Colour($this->labelfontshadowcolour);
			if ($shcol->is_real())
			{
				$map->myimagestring($node_im, $this->labelfont, $txt_x + 1, $txt_y + 1, $this->proclabel, $shcol->gdallocate($node_im),$this->labelangle);
			}

			$txcol = new Colour($this->labelfontcolour[0],$this->labelfontcolour[1],$this->labelfontcolour[2]);
			#$col=myimagecolorallocate($node_im, $this->labelfontcolour[0], $this->labelfontcolour[1],
			#	$this->labelfontcolour[2]);
			if($txcol->is_contrast())
			{
				if($col->is_real())
				{
					$txcol = $col->contrast();
				}
				else
				{
					warn("You can't make a contrast with 'none'.\n");
					$txcol = new Colour(0,0,0);
				}
			}
			$map->myimagestring($node_im, $this->labelfont, $txt_x, $txt_y, $this->proclabel, $txcol->gdallocate($node_im),$this->labelangle);
			//$map->myimagestring($node_im, $this->labelfont, $txt_x, $txt_y, $this->proclabel, $txcol->gdallocate($node_im),90);
		}

		# imagerectangle($node_im,$label_x1,$label_y1,$label_x2,$label_y2,$map->black);
		# imagerectangle($node_im,$icon_x1,$icon_y1,$icon_x2,$icon_y2,$map->black);

		$map->nodes[$this->name]->centre_x = $this->x - $bbox_x1;
		$map->nodes[$this->name]->centre_y = $this->y - $bbox_y1;

		if(1==0)
		{

			imageellipse($node_im, $this->centre_x, $this->centre_y, 8, 8, $map->selected);

			foreach (array("N","S","E","W","NE","NW","SE","SW") as $corner)
			{
				list($dx, $dy)=calc_offset($corner, $this->width, $this->height);
				imageellipse($node_im, $this->centre_x + $dx, $this->centre_y + $dy, 5, 5, $map->selected);
			}
		}

		# $this->image = $node_im;
		$map->nodes[$this->name]->image = $node_im;
	}

	function update_cache($cachedir,$mapname)
	{
		$cachename = $cachedir."/node_".md5($mapname."/".$this->name).".png";
		// save this image to a cache, for the editor
		imagepng($this->image,$cachename);
	}

	// draw the node, using the pre_render() output
	function NewDraw($im, &$map)
	{
		// take the offset we figured out earlier, and just blit
		// the image on. Who says "blit" anymore?

		// it's possible that there is no image, so better check.
		if(isset($this->image))
		{
			imagealphablending($im, true);
			imagecopy ( $im, $this->image, $this->x - $this->centre_x, $this->y - $this->centre_y, 0, 0, imagesx($this->image), imagesy($this->image) );
		}

	}

	// take the pre-rendered node and write it to a file so that
	// the editor can get at it.
	function WriteToCache()
	{
	}

	function Reset(&$newowner)
	{
		$this->owner=$newowner;
		$template = $this->template;
        
		if($template == '') $template = "DEFAULT";

		debug("Resetting $this->name with $template\n");
		
		// the internal default-default gets it's values from inherit_fieldlist
		// everything else comes from a node object - the template.
		if($this->name==':: DEFAULT ::')
		{
			foreach (array_keys($this->inherit_fieldlist)as
				$fld) { $this->$fld=$this->inherit_fieldlist[$fld]; }
		}
		else
		{
			$this->CopyFrom($this->owner->nodes[$template]); 
		}
		$this->template = $template;
		
		// to stop the editor tanking, now that colours are decided earlier in ReadData
		$this->colours[IN] = new Colour(192,192,192);
		$this->colours[OUT] = new Colour(192,192,192);
		
		$this->id = $newowner->next_id++;
	}

	function CopyFrom(&$source)
	{
		debug("Initialising NODE $this->name from $source->name\n");
		assert('is_object($source)');
		
		foreach (array_keys($this->inherit_fieldlist)as $fld) {
			if($fld != 'template') $this->$fld=$source->$fld;
		}
	}

	function WriteConfig()
	{
		$output='';
				
		# $output .= "# ID ".$this->id." - first seen in ".$this->defined_in."\n";

		// This allows the editor to wholesale-replace a single node's configuration
		// at write-time - it should include the leading NODE xyz line (to allow for renaming)
		if($this->config_override != '')
		{
			$output  = $this->config_override."\n";
		}
		else
		{
			# $defdef = $this->owner->defaultnode;
			$dd = $this->owner->nodes[$this->template];
			
			debug("Writing config for NODE $this->name against $this->template\n");
			
			# $field = 'zorder'; $keyword = 'ZORDER';
			$basic_params = array(
					# array('template','TEMPLATE',CONFIG_TYPE_LITERAL),
					array('label','LABEL',CONFIG_TYPE_LITERAL),
					array('zorder','ZORDER',CONFIG_TYPE_LITERAL),
					array('labeloffset','LABELOFFSET',CONFIG_TYPE_LITERAL),
					array('labelfont','LABELFONT',CONFIG_TYPE_LITERAL),
					array('labelangle','LABELANGLE',CONFIG_TYPE_LITERAL),
					array('overlibwidth','OVERLIBWIDTH',CONFIG_TYPE_LITERAL),
					array('overlibheight','OVERLIBHEIGHT',CONFIG_TYPE_LITERAL),

					array('aiconoutlinecolour','AICONOUTLINECOLOR',CONFIG_TYPE_COLOR),
					array('aiconfillcolour','AICONFILLCOLOR',CONFIG_TYPE_COLOR),
					array('labeloutlinecolour','LABELOUTLINECOLOR',CONFIG_TYPE_COLOR),
					array('labelfontshadowcolour','LABELFONTSHADOWCOLOR',CONFIG_TYPE_COLOR),
					array('labelbgcolour','LABELBGCOLOR',CONFIG_TYPE_COLOR),
					array('labelfontcolour','LABELFONTCOLOR',CONFIG_TYPE_COLOR)
				);
			
			# TEMPLATE must come first. DEFAULT
			if($this->template != 'DEFAULT' && $this->template != ':: DEFAULT ::')
			{
				$output.="\tTEMPLATE " . $this->template . "\n";
			}
			
			foreach ($basic_params as $param)
			{
				$field = $param[0];
				$keyword = $param[1];

			#	$comparison=($this->name == 'DEFAULT' ? $this->inherit_fieldlist[$field] : $defdef->$field);
				if ($this->$field != $dd->$field)
				{
					if($param[2] == CONFIG_TYPE_COLOR) $output.="\t$keyword " . render_colour($this->$field) . "\n";
					if($param[2] == CONFIG_TYPE_LITERAL) $output.="\t$keyword " . $this->$field . "\n";
				}
			}

			// IN/OUT are the same, so we can use the simpler form here
#			print_r($this->infourl);
			#$comparison=($this->name == 'DEFAULT'
			#? $this->inherit_fieldlist['infourl'][IN] : $defdef->infourl[IN]);
			if ($this->infourl[IN] != $dd->infourl[IN]) { $output.="\tINFOURL " . $this->infourl[IN] . "\n"; }
			
			#$comparison=($this->name == 'DEFAULT'
			#? $this->inherit_fieldlist['overlibcaption'][IN] : $defdef->overlibcaption[IN]);
			if ($this->overlibcaption[IN] != $dd->overlibcaption[IN]) { $output.="\tOVERLIBCAPTION " . $this->overlibcaption[IN] . "\n"; }

			// IN/OUT are the same, so we can use the simpler form here
			# $comparison=($this->name == 'DEFAULT'
			# ? $this->inherit_fieldlist['notestext'][IN] : $defdef->notestext[IN]);
			if ($this->notestext[IN] != $dd->notestext[IN]) { $output.="\tNOTES " . $this->notestext[IN] . "\n"; }

			# $comparison=($this->name == 'DEFAULT'
			# ? $this->inherit_fieldlist['overliburl'][IN] : $defdef->overliburl[IN]);
			if ($this->overliburl[IN] != $dd->overliburl[IN]) { $output.="\tOVERLIBGRAPH " . join(" ",$this->overliburl[IN]) . "\n"; }

			$val = $this->iconscalew. " " . $this->iconscaleh. " " .$this->iconfile;

			$comparison = $dd->iconscalew. " " . $dd->iconscaleh . " " . $dd->iconfile;

			if ($val != $comparison) {
				$output.="\tICON ";
				if($this->iconscalew > 0) {
					$output .= $this->iconscalew." ".$this->iconscaleh." ";
				}
				$output .= ($this->iconfile=='' ?  'none' : $this->iconfile) . "\n";
			}

			# $comparison=($this->name == 'DEFAULT'
			# ? $this->inherit_fieldlist['targets'] : $defdef->targets);

			if ($this->targets != $dd->targets)
			{
				$output.="\tTARGET";

				foreach ($this->targets as $target) { 
					if(strpos($target[4]," ") == FALSE) 
					{
						$output.=" " . $target[4]; 
					}
					else
					{
						$output.=' "' . $target[4].'"'; 
					}
				}
				

				$output.="\n";
			}

		#	$comparison = ($this->name == 'DEFAULT' ? $this->inherit_fieldlist['usescale'] : $defdef->usescale) . " " .
		#		($this->name == 'DEFAULT' ? $this->inherit_fieldlist['scalevar'] : $defdef->scalevar);
			$val = $this->usescale . " " . $this->scalevar;
			$comparison = $dd->usescale . " " . $dd->scalevar;

			if ( ($val != $comparison) ) { $output.="\tUSESCALE " . $val . "\n"; }

#			$comparison = ($this->name == 'DEFAULT'
#				? $this->inherit_fieldlist['useiconscale'] : $defdef->useiconscale) . " " .
#				($this->name == 'DEFAULT' ? $this->inherit_fieldlist['iconscalevar'] : $defdef->iconscalevar);
			$val = $this->useiconscale . " " . $this->iconscalevar;
			$comparison= $dd->useiconscale . " " . $dd->iconscalevar;
				
			if ( $val != $comparison) { $output.="\tUSEICONSCALE " .$val . "\n"; }

			#$comparison = ($this->name == 'DEFAULT'
			#? $this->inherit_fieldlist['labeloffsetx'] : $defdef->labeloffsetx) . " " . ($this->name == 'DEFAULT'
		#		? $this->inherit_fieldlist['labeloffsety'] : $defdef->labeloffsety);
			$val = $this->labeloffsetx . " " . $this->labeloffsety;
			$comparison = $dd->labeloffsetx . " " . $dd->labeloffsety;

			if ($comparison != $val ) { $output.="\tLABELOFFSET " . $val . "\n"; }

			#$comparison=($this->name == 'DEFAULT' ? $this->inherit_fieldlist['x'] : $defdef->x) . " " . 
			#			($this->name == 'DEFAULT' ? $this->inherit_fieldlist['y'] : $defdef->y);
			$val = $this->x . " " . $this->y;
			$comparison = $dd->x . " " . $dd->y;
			
			if ($val != $comparison)
			{
				if($this->relative_to == '')
				{ $output.="\tPOSITION " . $val . "\n"; }
				else
				{
					if($this->polar)
					{
						$output .= "\tPOSITION ".$this->relative_to . " " .  $this->original_x . "r" . $this->original_y . "\n";
					}
					else
					{
						$output.="\tPOSITION " . $this->relative_to . " " .  $this->original_x . " " . $this->original_y . "\n";
					}
				}
			}

			if (($this->max_bandwidth_in != $dd->max_bandwidth_in)
				|| ($this->max_bandwidth_out != $dd->max_bandwidth_out)
					|| ($this->name == 'DEFAULT'))
			{
				if ($this->max_bandwidth_in == $this->max_bandwidth_out)
				{ $output.="\tMAXVALUE " . $this->max_bandwidth_in_cfg . "\n"; }
				else { $output
				.="\tMAXVALUE " . $this->max_bandwidth_in_cfg . " " . $this->max_bandwidth_out_cfg . "\n"; }
			}

			foreach ($this->hints as $hintname=>$hint)
			{
			  // all hints for DEFAULT node are for writing
			  // only changed ones, or unique ones, otherwise
			      if(
			    ($this->name == 'DEFAULT')
			  ||
				    (isset($dd->hints[$hintname])
				    &&
				    $dd->hints[$hintname] != $hint)
				  ||
				    (!isset($dd->hints[$hintname]))
				)
			      {
			    $output .= "\tSET $hintname $hint\n";
			      }
			}
			if ($output != '')
			{
				$output = "NODE " . $this->name . "\n$output\n";
			}
		}
		return ($output);
	}

	function asJS()
	{
		$js = '';
		$js .= "Nodes[" . js_escape($this->name) . "] = {";
		$js .= "x:" . (is_null($this->x)? "'null'" : $this->x) . ", ";
		$js .= "y:" . (is_null($this->y)? "'null'" : $this->y) . ", ";
		$js .= "\"id\":" . $this->id. ", ";
		// $js.="y:" . $this->y . ", ";
		$js.="ox:" . $this->original_x . ", ";
		$js.="oy:" . $this->original_y . ", ";
		$js.="relative_to:" . js_escape($this->relative_to) . ", ";
		$js.="label:" . js_escape($this->label) . ", ";
		$js.="name:" . js_escape($this->name) . ", ";
		$js.="infourl:" . js_escape($this->infourl[IN]) . ", ";
		$js.="overlibcaption:" . js_escape($this->overlibcaption[IN]) . ", ";
		$js.="overliburl:" . js_escape(join(" ",$this->overliburl[IN])) . ", ";
		$js.="overlibwidth:" . $this->overlibheight . ", ";
		$js.="overlibheight:" . $this->overlibwidth . ", ";
		if(preg_match("/^(none|nink|inpie|outpie|box|rbox|gauge|round)$/",$this->iconfile))
		{
			$js.="iconfile:" . js_escape("::".$this->iconfile);
		}
		else
		{
			$js.="iconfile:" . js_escape($this->iconfile);
		}
		
		$js .= "};\n";
		$js .= "NodeIDs[\"N" . $this->id . "\"] = ". js_escape($this->name) . ";\n";
		return $js;
	}

	function asJSON($complete=TRUE)
	{
		$js = '';
		$js .= "" . js_escape($this->name) . ": {";
		$js .= "\"id\":" . $this->id. ", ";
		$js .= "\"x\":" . ($this->x - $this->centre_x). ", ";
		$js .= "\"y\":" . ($this->y - $this->centre_y) . ", ";
		$js .= "\"cx\":" . $this->centre_x. ", ";
		$js .= "\"cy\":" . $this->centre_y . ", ";
		$js .= "\"ox\":" . $this->original_x . ", ";
		$js .= "\"oy\":" . $this->original_y . ", ";
		$js .= "\"relative_to\":" . js_escape($this->relative_to) . ", ";
		$js .= "\"name\":" . js_escape($this->name) . ", ";
		if($complete)
		{
			$js .= "\"label\":" . js_escape($this->label) . ", ";
			$js .= "\"infourl\":" . js_escape($this->infourl) . ", ";
			$js .= "\"overliburl\":" . js_escape($this->overliburl) . ", ";
			$js .= "\"overlibcaption\":" . js_escape($this->overlibcaption) . ", ";

			$js .= "\"overlibwidth\":" . $this->overlibheight . ", ";
			$js .= "\"overlibheight\":" . $this->overlibwidth . ", ";
			$js .= "\"iconfile\":" . js_escape($this->iconfile). ", ";
		}
		$js .= "\"iconcachefile\":" . js_escape($this->cachefile);
		$js .= "},\n";
		return $js;
	}
};

// vim:ts=4:sw=4:
?>
